
#include <string.h>
#include <iostream>
using namespace std;

#define MAX_REMIND 50   //最大提醒次数
#define MSG_LEN 60      //提醒消息的最大长度

/*
  fun01 = printf() || scanf() fgets() getchar() 函数使用说明
  13.5节的程序remind.c (指针数组,malloc)
*/

//remind(提醒) 
void remind();

int main(){
  remind();
  return 0;
}

//13.5节的程序remind.c
void remind(){
  char *reminders[MAX_REMIND]; //指针数组
  char day_str[3],msg_str[MSG_LEN + 1];

  int day,i,j,num_remind = 0;

  for(;;){
    if(num_remind == MAX_REMIND){
      break;
    }

    cout << "输入日期和提醒: "<< endl; 
    scanf("%2d",&day); //即使输入有更多的数字，在%与d之间的数2也会通知scanf函数在读入两个数字后停止。
    
    if(day == 0){
      break;
    }
    sprintf(day_str,"%2d",day); // sprintf 只读取前两位 把其他字符留在了缓冲区。
    fgets(msg_str,MSG_LEN,stdin); // 直接读取缓冲区内容 

    //判断编号是否重复
    for (i = 0; i < num_remind; i++)
    { 
      //若参数s1和s2字符串相同则返回0，s1若大于s2则返回大于0的值，s1若小于s2则返回小于0的值
      if(strcmp(day_str,reminders[i]) < 0){
        break;
      }
    }

    for(j = num_remind; j > i; j--){
      //strcpy(reminders[j],reminders[j - 1]);
      //不需要为了给新提醒分配空间而调用函数strcpy来移动已有的字符串，只需要移动指向字符串的指针。
      reminders[j] = reminders[j - 1];
    }

    reminders[i] = (char*)malloc(2 + strlen(msg_str) + 1);

    if(reminders[i] == NULL){
      break;
    }

    strcpy(reminders[i],day_str);
    strcat(reminders[i],msg_str);

    num_remind++;
  }

  for (size_t i = 0; i < num_remind; i++)
  {
    printf("%s",reminders[i]);
  }
  

}



//fun01 ==== printf() || scanf() fgets() getchar() 函数使用说明
void fun01(){

  char *str;
  cout << "请输入字符串: "<< endl; 

  //换行符会使scanf函数停止读入，空格符或制表符也会产生同样的结果。为了一次读入一整行输入，可以使用gets函数。

  //gets()这个函数只有一个参数，那就是字符缓冲区的指针，并没有指定该缓冲区的长度也就是大小。
  //当你输入一个很长很长的字符串的时候，gets ()函数会把每一个字符都存入到栈(statck)中，因此当字符串很大的时候就会导致程序异常终止。
  //gets(str);

  fgets(str,20,stdin);
  cout << "读取的字符串: " << str << endl;


  cout << "请输入字符: "<< endl;  
  char ch1;
  char ch2;
  ch1 = getchar();  //每次调用getchar函数时，它会读入一个字符并将其返回。
  ch2 = getchar();
  cout << "读取的字符: " << ch1 << ","<< ch2 << endl;

  /*
    printf(格式串,表达式1,表达式2,....)

    格式串包含普通字符和转换说明（conversion specification）​，其中转换说明以字符%开头。

    转换说明 %d 指定printf函数把int型值从二进制形式转换成十进制数字组成的字符串
    转换说明 %f 对float型值也进行类似的转换。

    我们可以用%.1f来显示小数点后带一位数字的float型值
    
    == 转换说明 ==
    m和p都是整数常量，而X是字母。m和p都是可选的。如果省略p，m和p之间的小数点也要去掉。
    在转换说明%10.2f中，m是10，p是2，而X是f。
    在转换说明%10f中，m是10，p（连同小数点一起）省去了；
    而在转换说明%.2f中，p是2，m省去了。

    最小字段宽度（minimum field width）m 指定了要显示的最少字符数量。

    m指定了要显示的最少字符数量。如果要显示的数值所需的字符数少于m，那么值在字段内是右对齐的。​（换句话说，在值前面放置额外的空格。​）

    转换说明%4d将以•123的形式显示数123（用符号•表示空格字符）​。
    在m前放上一个负号会导致左对齐；转换说明%-4d将以123•的形式显示123。

    如果要显示的值所需的字符数多于m，那么字段宽度会自动扩展为所需的尺寸。因此，转换说明%4d将以12345的形式显示数12345，而不会丢失数字。


    精度（precision）p的含义很难描述，因为它依赖于转换说明符（conversion specifier）x的选择。

    d ——表示十进制（基数为10）形式的整数。p指明了待显示的数字的最少个数（必要时在数前加上额外的零）​；如果省略p，则默认它的值为1。
    e ——表示指数（科学记数法）形式的浮点数。p指明了小数点后应该出现的数字的个数（默认值为6）​。如果p为0，则不显示小数点。
    f ——表示“定点十进制”形式的浮点数，没有指数。p的含义与在说明符e中的一样。

    %5.3d —— 以十进制形式显示变量i，且至少占用5个字符的空间并至少有3位数字。因为变量i只有2个字符长度，所以要添加一个额外的零来保证有3位数字。

    printf("%3d",23);   //.23 (.表示空格)
    printf("%03d",23);  //023
    printf("%.3d",23);  //023
    printf("%3.3d",23); //023
    printf("%3.4d",23); //0023
  */

}




















